package com.jiajia.study.nio;

import java.io.*;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 *
 * NIO  通道  demo:
 *
 * 一 通道：
 *      用户源节点与目标节点直接的连接，在NIO中负责缓冲区中数据的传输，channel本身不存储任何数据
 *
 * 二 通道的主要实现类：
 *      Channel 接口
 *              --FileChannel
 *              --SocketChannel
 *              --ServerChannel
 *              --DatagramChannel
 *
 * 三 获取通道：
 *      1 java 针对支持通道的类提供了 getChannel() 方法
 *          本地IO
 *          FileInputStream/FileOutPutStream
 *          RandomAccessFile
 *
 *          网络IO：
 *          Socket
 *          ServerSocket
 *          DatagramSocket
 *
 *      2 在 jdk1.7 中 NIO.2 针对各个通道提供了静态方法
 *
 *      3 在 jdk1.7 中 NIO.2 Files工具类的 newByteChannel()
 *
 * 四 通道之间的数据传输
 *
 *      transferFrom()
 *      transferTo()
 *
 * 五 分散（Scatter）与聚集（Gather）
 *
 *      分散读取（Scattering Reads）：将通道中的数据分散到多个缓冲区中
 *      剧集写入（Gathering Writes）：将多个缓冲区中的数据聚集到通道中
 *
 *
 * 六 字符集 charset
 *
 *      编码： 字符串 -》 字节数组
 *
 *      解码： 字节数组  -> 字符串
 *
 * @author : zjj
 * @date : 2021-04-20 17:22
 */
public class ChannelClient {
    public static void main(String[] args) throws IOException {
        // testChannel();
//        testChannelDirect();
//        testChannelTransfer();


//        testScatterAndGather();
       // testCharset();

        testCharset1();

    }

    /**
     * 测试室利用通道完成文件的赋值（非直接缓冲区）
     */
    private static void testChannel() throws IOException {
        FileInputStream fileInputStream = new FileInputStream(new File("test.mp4"));
        FileOutputStream fileOutputStream = new FileOutputStream(new File("beauty1.mp4"));

        // 1 获取通道
        FileChannel inputChannel = fileInputStream.getChannel();
        FileChannel outputChannel = fileOutputStream.getChannel();

        // 2 分配指定大小的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        // 3 将通道的数据填入到缓冲区中
        while(inputChannel.read(buffer) != -1){
            // 切换读取数据的模式
            buffer.flip();
            // 4 将缓冲区的数据写入到另外一个通道里面
            outputChannel.write(buffer);
            // 清空缓冲区
            buffer.clear();
        }


        inputChannel.close();
        outputChannel.close();
        fileInputStream.close();
        fileOutputStream.close();
    }

    /**
     * 直接缓冲区
     */
    private static void testChannelDirect()throws IOException{
        // 获取通道
        FileChannel inChannel = FileChannel.open(Paths.get("test.mp4"), StandardOpenOption.READ);
        FileChannel outChannel = FileChannel.open(Paths.get("beauty3.mp4"),
                StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);

        // 内存映射文件，直接缓冲区
        MappedByteBuffer inputMapBuffer = inChannel.map(FileChannel.MapMode.READ_ONLY,0,inChannel.size());
        MappedByteBuffer outputMapBuffer = outChannel.map(FileChannel.MapMode.READ_WRITE,0,inChannel.size());

        // 直接对缓冲区的数据进行读写
        byte[] buffer = new byte[inputMapBuffer.limit()];
        // 从直接缓冲区中读数据
        inputMapBuffer.get(buffer);
        // 往直接缓冲区中写数据
        outputMapBuffer.put(buffer);

        inChannel.close();
        outChannel.close();

    }

    /**
     * 通道直接的数据传输
     */
    private static void testChannelTransfer() throws IOException {

        FileChannel inputChannel = FileChannel.open(Paths.get("test.mp4"),StandardOpenOption.READ);
        FileChannel outputChannel = FileChannel.open(Paths.get("beauty4.mp4"),
                StandardOpenOption.WRITE,StandardOpenOption.READ,StandardOpenOption.CREATE);
        //从 一个通道传入到另外一个通道
        inputChannel.transferTo(0,inputChannel.size(),outputChannel);
        //
        // outputChannel.transferFrom(inputChannel,0,inputChannel.size());

        inputChannel.close();
        outputChannel.close();

    }

    /**
     * 测试聚集和分散
     */
    private static void testScatterAndGather() throws IOException {

        RandomAccessFile randomAccessFile = new RandomAccessFile("hello2.txt","rw");
        // 1 获取通道
        FileChannel channel = randomAccessFile.getChannel();

        // 2 分配指定大小的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(100);
        ByteBuffer buffer1 = ByteBuffer.allocate(1024);

        // 3 分散读取
        ByteBuffer[] byteBuffers = {buffer,buffer1};

        channel.read(byteBuffers);


        for (ByteBuffer byteBuffer : byteBuffers){
            byteBuffer.flip();
        }
        System.out.println(new String(byteBuffers[0].array(), 0, byteBuffers[0].limit()));
        System.out.println("-----------------");
        System.out.println(new String(byteBuffers[1].array(), 0, byteBuffers[1].limit()));


        // 聚集写入
        RandomAccessFile randomAccessFile1 = new RandomAccessFile("2.txt","rw");
        FileChannel channel1 = randomAccessFile1.getChannel();

        channel1.write(byteBuffers);


    }

    /**
     * 测试编码集
     */
    private static void testCharset(){
        SortedMap<String, Charset> stringCharsetSortedMap = Charset.availableCharsets();

        Set<Map.Entry<String, Charset>> entries = stringCharsetSortedMap.entrySet();

        for(Map.Entry<String,Charset> entry : entries){
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }

    /**
     * 测试编码解码
     */
    private static void testCharset1() throws CharacterCodingException {
        Charset charset = Charset.forName("GBK");

        // 获取编码器
        CharsetEncoder charsetEncoder = charset.newEncoder();

        // 获取解码器
        CharsetDecoder charsetDecoder = charset.newDecoder();

        CharBuffer buffer = CharBuffer.allocate(1024);
        buffer.put("you can you do it ");
        //切换成读取模式，position位置为0
        buffer.flip();

        // 编码
        ByteBuffer byteBuffer = charsetEncoder.encode(buffer);

        for (int i = 0; i < 18; i++) {
            System.out.println(byteBuffer.get());
        }
        // 解码
        byteBuffer.flip();

        CharBuffer decode = charsetDecoder.decode(byteBuffer);

        System.out.println(decode.toString());





    }

}
